package taskmaster.controller;

import taskmaster.auth.User;
import taskmaster.job.QueryParams;
import taskmaster.job.Task;
import taskmaster.job.TaskBuilder;
import taskmaster.util.LookupLists;

import java.io.File;
import java.util.Date;


/**
 * taskmaster.controller.ViewTasks<BR>
 * <BR>
 * Controller for viewing tasks.  <BR>
 * Takes form data directly from .jsp, sends it to TaskBuilder, catches exceptions if any and returns a shiny new TaskCollection<BR>
 * <BR>
 * Scope: request (but could be session if you're thinking of making TaskCollection also session)
 */
public class ViewTasks
{
    /**
     * the format that dates must be in to be parsable int a java.util.Date object
     */
    private static String dateParseFormat = "MM/dd/yy";
    /**
     * Internally store a TaskBuilder object upon first creation of collection.  Calling the same instance of TaskBuilder allows it to perform updates to data instead of full recreates
     */
    private TaskBuilder builder = null;
    /** store the TaskCollection that viewTasks.jsp is currently using so that recreateCollection() can update it when necessary.
     */
//	private TaskCollection tc = null;
    /**
     * LookupLists provides abstraction from the db for a bunch of various db related data.
     * <BR>Stored here for a couple of method calls that further abstract the data a bit for ease of use when called from viewTasks.jsp
     */
    private LookupLists ll = null;
    /**
     * Exception as a string, if there was one, resulting from the getTaskCollection() call.
     *
     * @see getExceptions()
     */
    private String storedException = null;

    /**
     * Store the parameters that created the most recent collection.
     * <BR>Used when recreateCollection is called; its sent to TaskBuilder
     *
     * @see setLastSelectParams()
     */
    private QueryParams lastLimiters = null;
    private User forUser = null;

    /**
     * No arg constructor, the only one for this class.
     * Object has session scope, is instantiated by the jsp page. JSP's require that there be a no arg constructor.
     */
    public ViewTasks()
    {
        //
    }


    /** Method called by viewTasks.jsp that parses the form as to what Tasks to get for the user.
     * This is the first method called by viewTasks.jsp using the data submitted to it by selectView.jsp.
     * It acts as a controller between TaskBuilder and the webform since the webform just submits strings,
     * and TaskBuilder which expects whatever data in whatever format (hashtables, int arrays, etc) in order to create the collection.
     *
     *This method, unlike most others in taskmaster, doesn't throw its exceptions all the way up to the jsp page.  Rather,
     *it stores the exception that is accessible via getExceptions()
     *
     * @see getExceptions
     * @returns A TaskCollection filled with Tasks matching the parameters defined in selectView.jsp
     */
//	public TaskCollection createCollection(User user,
//											String[] formLimitToRole,
//											String[] formLimitToJob,
//											String[] formLimitToCategory,
//											String[] formLimitToType,
//											String formDate,
//											TaskCollection tc,
//											LookupLists ll
//											) {
//		try {
//			System.err.println(new java.util.Date().getTime() +" -beginning to parse request");
//			if (this.ll == null)
//				this.ll = ll;
//			this.ll.forceInstantiation(user);// force lists to be updated with every new query, instead of just once per session
//
////			this.tc = tc;
//			this.storedException = null;	// initialize this back to null
//
//			int[] limitToRole = null;
//			String[] limitToJob = null;
//			Hashtable limitToCategory = null;
//			char[] limitToType = null;
//			Date limitToDate = null;
//
//			if (formLimitToRole != null && formLimitToRole.length > 0) {
//				limitToRole = new int[formLimitToRole.length];
//				for (int i=0; i<limitToRole.length; i++)
//					limitToRole[i] = Integer.parseInt(formLimitToRole[i]);
//			}
//			if (formLimitToJob != null && formLimitToJob.length > 0)
//				limitToJob = formLimitToJob;
//			if (formLimitToCategory != null && formLimitToCategory.length > 0) {
//				limitToCategory = new Hashtable();
//				for (int i=0; i<formLimitToCategory.length; i++)
//					limitToCategory.put(formLimitToCategory[i], formLimitToCategory[i]);
//				// oustanding overrides all other categories and gives view of everything but completed
//				if (limitToCategory.get("outstanding") != null) {
//					limitToCategory.remove("outstanding");
//					limitToCategory.put("needsUser", "needsUser");
//               //					limitToCategory.put("cron", "cron");
//					limitToCategory.put("error", "error");
//					limitToCategory.put("running", "running");
//					limitToCategory.put("blocked", "blocked");
//				}
//			}
//			if (formLimitToType != null && formLimitToType.length > 0) {
//				limitToType = new char[formLimitToType.length];
//				for (int i=0; i<limitToType.length; i++) {
//					if (formLimitToType[i].length() != 1)
//						throw new NumberFormatException("taskmaster.controller.ViewTasks:createCollection: Status has to be a String of length 1.  The invalid string was: "+ formLimitToType[i]);
//					limitToType[i] = formLimitToType[i].charAt(0);
//				}
//			}
//			if (formDate != null && formDate.length() > 0) {
//				// make new Date out of formDate
//				SimpleDateFormat df = new SimpleDateFormat();
//				df.applyPattern(dateParseFormat);
//				limitToDate = df.parse(formDate, new ParsePosition(0));
//			}
//
//			if (this.builder == null)
//				this.builder = new TaskBuilder(this.ll);	//has same scope as ViewTasks (session)
//
//			QueryParams limiters = new QueryParams(limitToRole, limitToJob, limitToCategory, limitToType, limitToDate, false);
//			this.setLastSelectParams(limiters, user);
//			SessionPersist.setQueryParamCookie(limiters, response);
//
//			System.err.println(new java.util.Date().getTime() +" -parsed request, sending it to builder");
//			return tc = this.builder.getTaskCollection(limiters, user, tc);
//		} catch (Exception e) {
//			String expl = "taskmaster.controller.ViewTasks:createCollection(): "+ e;
//			System.err.println(expl);
//			this.storedException = expl;
//			return null;
//		}
//	}

    /**
     * sets the passed queryParams as the internally stored one.
     *
     * @param limiters the queryParams object that created the current TaskCollection
     */
    private void setLastSelectParams(QueryParams limiters, User user)
    {
        this.lastLimiters = limiters;
        this.forUser = user;
    }

    /** Update the Tasks in this Collection to reflect the most recent statuses in the db.
     *<BR>
     * recreates the Tasks inside TaskCollection based on the params set the last time it was called.
     * Every time viewTasks get sent back to the server, this method should be called.
     * As the parameters used to create the Collection, it uses the same one that was used originally.
     * Calling this method should trigger the update part of TaskBuilder rather than the create so that a faster update is used rather than a full recreation.
     * @returns the same TaskCollection object as before, but with Tasks that reflect the most recent status of the db.
     */
//	public TaskCollection recreateCollection(User user,
//											LookupLists ll,
//											TaskCollection tc,
//											HttpServletRequest request, HttpServletResponse response) throws Exception {
//		if (this.lastLimiters == null)
//			this.lastLimiters = SessionPersist.getQueryParamsFromCookie(request);
//		if (this.lastLimiters == null)
//			return null;
//
//		if (this.ll == null)
//			this.ll = ll;
//		this.ll.setOwner(user);
//		if (this.builder == null)
//			this.builder = new TaskBuilder(this.ll);	//has scope of ViewTasks (session)
//		this.forUser = user;
//
//		System.err.println(new Date().getTime() +" -recreateCollection called");
//		tc = this.builder.getTaskCollection(this.lastLimiters, this.forUser, tc);
//		return tc;
//	}

    /**
     * returns any exception that may have been raised during the processing of the request.
     * <BR>
     * Using this instead of throwing the exception all the way to the jsp layer.
     *
     * @see createCollection
     */
    public String getExceptions()
    {
        return this.storedException;
    }

    /**
     * true if any exceptions were thrown from anyplace during the creation of the TaskCollection; false otherwise.
     *
     * @return true if any exceptions were thrown from anyplace during the creation of the TaskCollection; false otherwise.
     */
    public boolean hasErrors()
    {
        return (this.storedException == null) ? false : true;
    }

    /**
     * matches statusId to its name in tms.task_statuses table.  These names are in the past tense, denoting the status for a Task.
     * This is unlike the ones in tms.actions which are active, denoting actions that a user can take on this Task.
     *
     * @see findSpecificStatusNameAction
     */
    public String findSpecificStatusName(String statusId) throws Exception
    {
        return (String) ll.getAllStatusesHash().get(statusId);
    }

    /**
     * finds a specific status name using the action table display names from tms.actions.  These names are active, denoting things that a user can do to a Task.
     * This is unlike the names in tms.task_statuses, which are past tense, denoting the status for a Task.
     *
     * @see findSpecificStatusName
     */
    public String findSpecificStatusNameAction(Task task, int toStatusInt) throws Exception
    {
        String toStatus = String.valueOf(toStatusInt);
        String status = String.valueOf(task.getStatusId());
        String type = task.getType() + "";
        String[][] allAllowedActions = ll.getAllAllowedActions();
        for (int i = 0; i < allAllowedActions.length; i++)
        {
            if (
                    status.equals(allAllowedActions[i][0]) &&         // same status
                            type.equals(allAllowedActions[i][2]) &&         // same type
                            toStatus.equals(allAllowedActions[i][1])        // has the same toStatus
                    )
            {
                return allAllowedActions[i][3];
            }
        }
        return null;
    }

    /**
     * converts the roleId int to its string value, such as statArb, pricing, etc.
     *
     * @param roleId a roleId that exists in the tms.roles table
     * @return the role that is represented in TMS by this roleId
     */
    public String getRole(int roleId)
    {
        String role = String.valueOf(roleId);
        return (String) ll.getAllRolesHash().get(role);
    }

    /**
     * converts the roleId int inside this Task to its string value, such as statArb, pricing, etc.
     *
     * @param task a roleId that exists in the tms.roles table
     * @return the role that is represented in TMS by this roleId
     * @throws Exception NullPointerException thrown if Task is null
     */
    public String getRole(Task task)
    {
        String roleId = String.valueOf(task.getRoleId());
        return (String) ll.getAllRolesHash().get(roleId);
    }

    /**
     * get the full name of this type represented in TMS by this char
     */
    public String getTypeName(char type) throws Exception
    {
        return (String) ll.getAllTypesHash().get(String.valueOf(type));
    }

    /**
     * get the full name of the type of Task that is represented in TMS by task.getType() in this task.
     */
    public String getTypeName(Task task) throws Exception
    {
        return (String) ll.getAllTypesHash().get(String.valueOf(task.getType()));
    }

    /**
     * gets all allowed statuses (retry, run, continue anyway, etc) that can be done for this task.
     * asserts that this list is for an administrator.
     *
     * @see getAllowedActions()
     */
    public String[][] getAllowedStatuses(User user, Task task) throws Exception
    {
        return this.getAllowedActions(user, task, true);                // all valid statuses; you have admin access!
    }

    /**
     * gets all allowed actions (retry, run, continue anyway, etc) that can be done for this task.
     * Note that this is different than admin access for changing the status, though they overlap.
     * appears as the 'action' column, and doesn't depend on access to the system.
     * It assumes that the caller has already figured out that it has write or admin access
     * <p/>
     * Instead of looking in the db each time, this searches an array filled for matching actions...
     *
     * @see getAllowedStatuses()
     */
    public String[][] getAllowedActions(User user, Task task) throws Exception
    {
        return this.getAllowedActions(user, task, false);
    }

    /**
     * Called internally by getAllowedActions() & getAllowedStatuses() to return list of actions that the user can take on this Task.
     *
     * @param user User for whom the transitions are for
     * @return 2d array of actions available for this Task; null if user has no write access.
     * @throws Exception thrown if user is null or task is null with message <I>"Cannot get status for null Task or null User."</I>
     * @see getAllowedActions()
     * @see getAllowedStatuses()
     */
    private String[][] getAllowedActions(User user, Task task, boolean forAdmin) throws Exception
    {
        if (user == null || task == null)
        {
            throw new Exception("taskmaster.controller.ViewTasks:getAllowedStatuses: Cannot get status for null Task or null User.");
        }
        if (!task.isWritable())
        {
            return null;
        }
        String status = String.valueOf(task.getStatusId());
        String type = String.valueOf(task.getType());
        int h = 0;
        String[][] allAllowedActions = ll.getAllAllowedActions();
        String[][] actions = new String[allAllowedActions.length][2];
        for (int i = 0; i < allAllowedActions.length; i++)
        {
            if (
                    status.equals(allAllowedActions[i][0]) &&         // same status
                            type.equals(allAllowedActions[i][2]) &&         // same type
                            ("0".equals(allAllowedActions[i][4]) || forAdmin)// not an action that needs admin
                    )
            {
                actions[h][0] = allAllowedActions[i][1];
                actions[h][1] = allAllowedActions[i][3];
                h++;
            }
        }
        return actions;
    }

    /**
     * simply returns the proper format that dates have to be in for the web form
     *
     * @return the proper format that dates have to be in for the web form
     */
    public String getHowToFormatDate()
    {
        return dateParseFormat; // should use SimpleDateFormat.toPattern()
    }

    /**
     * quick conversion of statusId to statusname
     */
    public String getDependenciesStatus(Task dep) throws Exception
    {
        String statusId = String.valueOf(dep.getStatusId());
        return (String) ll.getAllStatusesHash().get(statusId);
    }

    /**
     * Determine the path to this Task's log file for the given date.  Doesn't check if the log file exists.
     * darrell wrote 20040719
     *
     * @see this.findLogFilePath()
     */
    public static String findLogFilePath(Task task, Date date)
    {
//         Properties prop=System.getProperties();
        String TMS_LOGDIR = System.getProperty("TMS_LOGDIR");
        if (TMS_LOGDIR == null)
        {
            throw new NullPointerException("taskmaster.controller.ViewTasks:findLogFilePath(): TMS_LOGDIR env var not set in System properties");
        }
        StringBuffer buf = new StringBuffer(TMS_LOGDIR);
        buf.append(File.separator).append(task.getJobTag());
        buf.append(File.separator).append(task.getTaskTag());

        buf.append(".");
        java.text.SimpleDateFormat s = new java.text.SimpleDateFormat("yyMMdd");
        s.format(date, buf, new java.text.FieldPosition(0));
        return buf.toString();
    }


    public static String findLogFilePath(String task,String job, Date date)
    {
//         Properties prop=System.getProperties();
        String TMS_LOGDIR = System.getProperty("TMS_LOGDIR");
        if (TMS_LOGDIR == null)
        {
            throw new NullPointerException("taskmaster.controller.ViewTasks:findLogFilePath(): TMS_LOGDIR env var not set in System properties");
        }
        StringBuffer buf = new StringBuffer(TMS_LOGDIR);
        buf.append(File.separator).append(job);
        buf.append(File.separator).append(task);

        buf.append(".");
        java.text.SimpleDateFormat s = new java.text.SimpleDateFormat("yyMMdd");
        s.format(date, buf, new java.text.FieldPosition(0));
        return buf.toString();
    }

    /**
     * Determine path to this Task's log file.  Doesn't check if the file exists.
     * <p/>
     * <BR><BR>path to log file is <B>[TMS_LOGDIR]/[task.jobTag()]/[task.taskTag()].[task rundate yyMMdd]</B>
     */
//    public static String findLogFilePath(Task task) throws Exception
//    {
//        return findLogFilePath(task, task.getDateOfStatus());
//    }

    /**
     * Tries to find a help file for this task.
     * <p/>
     * <BR><BR>file is looked for with the path <B>[TMS_HELPDIR]/[task.jobTag()]/[task.taskTag()].[txt || html || pdf || doc]</B>
     * <BR>
     * Any file, in this order, with this file ending will be found:
     * <UL>
     * <LI>.txt
     * <LI>.html
     * <LI>.pdf
     * <LI>.doc
     * </UL>
     * Any other file type or path will return null
     *
     * @param task Task to search
     * @return a File representing the helpfile for this Task, null if there is none.
     * @throws Exception if TMS_HELPDIR is not defined or other error
     */
    public File findHelpFilePath(String task,String job) throws Exception
    {
        String TMS_HELPDIR = System.getProperty("TMS_HELPDIR");
        if (TMS_HELPDIR == null)
        {
            throw new NullPointerException("taskmaster.controller.ViewTasks:findHelpFilePath(): TMS_HELPDIR env var not set in System properties");
        }
        StringBuffer findFile = new StringBuffer(TMS_HELPDIR);
        findFile.append(File.separator);
        findFile.append(job);
        File f = new File(findFile.toString(), task + ".txt");
        if (f.exists())
        {
            return f;
        }
        f = new File(findFile.toString(), task + ".html");
        if (f.exists())
        {
            return f;
        }
        f = new File(findFile.toString(), task + ".pdf");
        if (f.exists())
        {
            return f;
        }
        f = new File(findFile.toString(), task + ".doc");
        if (f.exists())
        {
            return f;
        }


        return null;
    }

    /**
     * Tries to find a help file for this task.
     * <p/>
     * <BR><BR>file is looked for with the path <B>[TMS_HELPDIR]/[jobTag]/JOB.[txt || html || pdf || doc]</B>
     * <BR>
     * Any file, in this order, with this file ending will be found:
     * <UL>
     * <LI>.txt
     * <LI>.html
     * <LI>.pdf
     * <LI>.doc
     * </UL>
     * Any other file type or path will return null
     *
     * @param task Task to search
     * @return a File representing the job's help file, null if there is none.
     * @throws Exception if TMS_HELPDIR is not defined or other error
     */
    public File findJobHelpFilePath(String jobTag) throws Exception
    {
        String TMS_HELPDIR = System.getProperty("TMS_HELPDIR");
        if (TMS_HELPDIR == null)
        {
            throw new NullPointerException("taskmaster.controller.ViewTasks:findHelpFilePath(): TMS_HELPDIR env var not set in System properties");
        }
        StringBuffer findFile = new StringBuffer(TMS_HELPDIR);
        findFile.append("/");
        findFile.append(jobTag);

        File f = new File(findFile.toString(), "JOB.txt");
        if (f.exists())
        {
            return f;
        }
        f = new File(findFile.toString(), "JOB.html");
        if (f.exists())
        {
            return f;
        }
        f = new File(findFile.toString(), "JOB.pdf");
        if (f.exists())
        {
            return f;
        }
        f = new File(findFile.toString(), "JOB.doc");
        if (f.exists())
        {
            return f;
        }
        return null;
    }


    /**
     * If the Task is a 'Verify' and its command field is not null, try to find a valid data file for it.
     * <BR><BR>
     * in the command field, the following vars and fields are parsed:
     * <UL>
     * <LI>$log - what would be the normal path to this Tasks log file
     * <LI>Any combination of $TMS_TASK, $TMS_JOB, $mmddyyyy, $yymmdd alongside regular strings.
     * <LI>after parsing vars, return result if path is a valid url
     * <LI>if after parsing string starts with a '/', prepend it with "TMS_LOGDIR" and return new string
     *
     * @return the path to this Task's data file as string or url, null if none defined.  Doesn't check to see if file or url exists.
     */
    public static String findDataFilePath(Task task) throws Exception
    {
        if (task == null ||
                task.getType() != 'V' ||
                task.getCommand() == null)
        {
            return null;
        }

        String rawcmd = task.getCommand();
        StringBuffer buf = new StringBuffer();
        rawcmd = rawcmd.trim();                    // get rid of white space at beginning and/or end
        if (rawcmd.equalsIgnoreCase("$log"))    // if just want log file
        {
//            return findLogFilePath(task);
        }        // use log as data file

        char[] cmdArray = rawcmd.toCharArray();
        int i = 0;
        while (i < cmdArray.length)
        {
            for (; i < cmdArray.length && cmdArray[i] != '$'; i++)
            {
                buf.append(cmdArray[i]);
            }                            // append char to buffer until find $ or end of string
            if (i < cmdArray.length && cmdArray[i] == '$')
            {            // found variable?
                i++;                                                // go to beginning of var found
                if (rawcmd.regionMatches(i, "TMS_TASK", 0, 8))
                {    // if the next 8 chars after $ match TMS_TASK
                    buf.append(task.getTaskTag());                    // replace var with value
                    i += 8;                                            // advance i past this var
                } else if (rawcmd.regionMatches(i, "TMS_JOB", 0, 7))
                {
                    buf.append(task.getJobTag());
                    i += 7;
                } else if (rawcmd.regionMatches(i, "mmddyyyy", 0, 8))
                {
                    java.text.SimpleDateFormat s = new java.text.SimpleDateFormat("MMddyyyy");
                    s.format(task.getDateOfStatus(), buf, new java.text.FieldPosition(0));
                    i += 8;
                } else if (rawcmd.regionMatches(i, "yymmdd", 0, 6))
                {
                    java.text.SimpleDateFormat s = new java.text.SimpleDateFormat("yyMMdd");
                    s.format(task.getDateOfStatus(), buf, new java.text.FieldPosition(0));
                    i += 6;
                } else
                {                                            // $ found but it wasn't a variable,
                    buf.append("$");                                // just write it and continue
                }
            }
        }
        if (buf.length() == 0)
        {
            return null;
        }
        try
        {                                                        // to make a valid url
            java.net.URL url = new java.net.URL(buf.toString());
            return url.toString();                                    // was able to do it
        } catch (java.net.MalformedURLException ignored) { }        // no valid url found
        if (buf.charAt(0) != '/')
        {
            String TMS_LOGDIR = System.getProperty("TMS_LOGDIR");
            if (TMS_LOGDIR == null)
            {
                throw new NullPointerException("taskmaster.controller.ViewTasks:findDataFilePath(): TMS_LOGDIR env var not set in System properties");
            }
            buf.insert(0, "/");
            buf.insert(0, TMS_LOGDIR);
        }
        return buf.toString();
    }


    public static void main(String args[])
    {

        // System.out.println(ViewTasks.findLogFilePath(null, Calendar.getInstance().getTime()));
//        ViewTasks vt=new ViewTasks();
//         File f=null;
//        try {
//          f=  vt.findHelpFilePath("ADJ_PRICE_CHECK");
//        } catch (Exception e) {
//            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
//        }
//        FileInputStream fis = null;
//        try {
//            fis = new FileInputStream(f);
//            BufferedReader br = new BufferedReader(new InputStreamReader(fis));
//        String strLine ;
//        while ((strLine = br.readLine()) != null)   {
//  // Print the content on the console
//  System.out.println (strLine);
//  }
//        } catch (IOException e) {
//            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
//        }


    }


}
